package com.heima.schedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.common.redis.CacheService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.dtos.TaskDto;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.schedule.Taskinfo;
import com.heima.model.common.pojos.schedule.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @Author Lijx
 * @Date 2024-07-26 10:48
 */
@Service
@Slf4j
public class TaskServiceImpl  implements TaskService {
    @Autowired
    private TaskinfoMapper taskinfoMapper;
    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;
    @Autowired
    private CacheService cacheService;

    /**
     * 添加任务
     * @param taskDto
     * @return
     */
    @Override
    public ResponseResult addTask(TaskDto taskDto) {
        //1. 先将任务添加到数据库
        addTaskToDB(taskDto);
        //2. 将任务添加到缓存
        addTaskToRedis(taskDto);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 将任务添加到缓存
     * @param taskDto
     */
    private void addTaskToRedis(TaskDto taskDto) {
        //拿到任务执行时间
        long executeTime = taskDto.getExecuteTime();
        //拿到当前系统时间
        long now = System.currentTimeMillis();
        //拿到预加载时间：当前系统时间+5分钟
        long futureTime = now + (1000*60*5);

        //2. 任务执行时间 <= 当前系统时间 ：添加到List
        if (executeTime <= now) {
            cacheService.lLeftPush("topic_"+taskDto.getTaskType()+"_"+taskDto.getPriority(), JSON.toJSONString(taskDto));
        }else if (executeTime <= futureTime){
            //3. 任务执行时间 > 当前系统时间 && <= 预加载时间 ：添加到ZSet
            cacheService.zAdd("future_"+taskDto.getTaskType()+"_"+taskDto.getPriority(), JSON.toJSONString(taskDto),executeTime);
        }
    }

    /**
     * 将任务添加到数据库
     * @param taskDto
     */
    private void addTaskToDB(TaskDto taskDto) {
        //将任务数据添加到taskinfo
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(taskDto,taskinfo);
        taskinfo.setExecuteTime(new Date(taskDto.getExecuteTime()));
        taskinfoMapper.insert(taskinfo);

        //将添加任务id回填,方便后期任务删除
        taskDto.setTaskId(taskinfo.getTaskId());

        //将任务数据添加到taskinfo_logs
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(taskinfo,logs);
        logs.setVersion(1);
        logs.setStatus(0);
        taskinfoLogsMapper.insert(logs);
    }

    /**
     * 根据任务优先级和类型拉取任务数据
     * @param taskType
     * @param priority
     * @return
     */
    @Override
    public ResponseResult pullTask(Integer taskType, Integer priority) {
        //1. 根据根据任务优先级和类型去redis的List队列中拉取对象任务数据
        String key = "topic_"+ taskType +"_" +priority;
        String taskDtoJSON = cacheService.lRightPop(key);

        if (StringUtils.isNotEmpty(taskDtoJSON)) {
            //2.1 将当前任务对应的DB中的Taskinfo表删除
            TaskDto taskDto = JSON.parseObject(taskDtoJSON, TaskDto.class);
            taskinfoMapper.deleteById(taskDto.getTaskId());

            //2.2 将当前任务对应的DB中TaskinfoLogs表中
            TaskinfoLogs logs = new TaskinfoLogs();
            logs.setTaskId(taskDto.getTaskId());
            logs.setStatus(1); // 1表示执行中
            taskinfoLogsMapper.updateById(logs);

            //3. 将当前任务信息返回
            return ResponseResult.okResult(taskDtoJSON);

        }else {

            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
    }

    //定时刷新ZSet数据到List中，任务执行频次：每1分钟执行一次
    @Scheduled(cron = "0 0/1 * * * ?")
    public void refreshZSetToList(){
        log.info("定时刷新ZSet数据到List中......");
        //查询所有的ZSet的key
        Set<String> zSetKeys = cacheService.scan("future_*");
        if (zSetKeys != null && zSetKeys.size() >0) {
            for (String key : zSetKeys) {
                Set<String> taskDtos = cacheService.zRangeByScore(key, 0, System.currentTimeMillis());

                //将这批到期的任务由ZSet集合转移到List集合
                String[] split = key.split("future_");
                String listKey = "topic_"+split[1];
                //将已经时间到期的这批任务：taskDtos从ZSet集合转移到List集合
                cacheService.refreshWithPipeline(key,listKey,taskDtos);
            }
        }

    }

    //定时刷新DB数据到Redis中，任务执行频次：每5分钟执行一次
    @Scheduled(cron = "0 0/5 * * * ?")
    public void refreshDBToRedis(){
        log.info("定时刷新DB数据到Redis中.....");
        //查询数据库中 执行时间 <= 预设时间 的任务数据都查询出来
        long futureTime = System.currentTimeMillis() + (1000*60*5);
        Date date = new Date(futureTime);
        List<Taskinfo> taskinfos = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().le(Taskinfo::getExecuteTime, date));
        if (taskinfos != null && taskinfos.size() > 0) {
            //遍历集合：拿到每一个任务
            for (Taskinfo taskinfo : taskinfos) {
                //封装TaskDto数据
                TaskDto taskDto = new TaskDto();
                BeanUtils.copyProperties(taskinfo,taskDto);
                taskDto.setExecuteTime(taskinfo.getExecuteTime().getTime());
                //添加任务到redis
                addTaskToRedis(taskDto);
            }
        }
    }
}
